package com.dji.sample.wayline.util;

import com.dji.sample.wayline.model.dto.KmzFileProperties;
import com.dji.sample.wayline.model.entity.Waypoint;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

public class KmzWaypointParser {

    private KmzWaypointParser() {}

    /**
     * 解析KMZ文件中的航点信息。
     * 逻辑与 WaylineFileServiceImpl#validKmzFile 一致：
     * - 校验后缀名为 .kmz
     * - 解压查找 wpmz/template.kml
     * - 使用DOM4J解析XML并提取航点字段
     *
     * @param file KMZ航线文件（MultipartFile）
     * @return List<Waypoint> 航点列表
     */
    public static List<Waypoint> parseWaypoint(MultipartFile file) {
        String filename = file.getOriginalFilename();
        if (Objects.nonNull(filename) && !filename.endsWith(KmzFileProperties.WAYLINE_FILE_SUFFIX)) {
            throw new RuntimeException("The file format is incorrect.");
        }
        try (ZipInputStream unzipFile = new ZipInputStream(file.getInputStream(), StandardCharsets.UTF_8)) {

            ZipEntry nextEntry = unzipFile.getNextEntry();
            while (Objects.nonNull(nextEntry)) {
                boolean isTemplateKml = (KmzFileProperties.FILE_DIR_FIRST + "/" + KmzFileProperties.FILE_DIR_SECOND_TEMPLATE).equals(nextEntry.getName());
                if (!isTemplateKml) {
                    nextEntry = unzipFile.getNextEntry();
                    continue;
                }
                SAXReader reader = new SAXReader();
                Document document = reader.read(unzipFile);
                if (!StandardCharsets.UTF_8.name().equals(document.getXMLEncoding())) {
                    throw new RuntimeException("The file encoding format is incorrect.");
                }

                List<Node> missionConfigs = document.selectNodes("//*[local-name()='missionConfig']");
                Node missionConfig = missionConfigs.get(0);
                List<Node> nodes = missionConfig.selectNodes("*[local-name()='takeOffSecurityHeight']");
                Node node = nodes.get(0);
                String takeOffSecurityHeight = node.getText();


                List<Waypoint> waypoints = new ArrayList<>();
                List<Node> placemarkNodes = document.selectNodes("//*[local-name()='Placemark']");
                for (Node placemark : placemarkNodes) {
                    List<Node> coordinatesNodes = placemark.selectNodes("*[local-name()='Point']/*[local-name()='coordinates']");
                    List<Node> indexNodes = placemark.selectNodes("*[local-name()='index']");
                    List<Node> heightNodes = placemark.selectNodes("*[local-name()='height']");
                    List<Node> speedNodes = placemark.selectNodes("*[local-name()='useGlobalSpeed']");
                    List<Node> headingNodes = placemark.selectNodes("*[local-name()='useGlobalHeadingParam']");
                    List<Node> gimbalNodes = placemark.selectNodes("*[local-name()='gimbalPitchAngle']");

                    int count = coordinatesNodes.size();
                    for (int i = 0; i < count; i++) {
                        String coordText = coordinatesNodes.get(i).getText().trim();
                        String[] parts = coordText.split(",");
                        if (parts.length < 2) {
                            continue;
                        }
                        Waypoint wp = new Waypoint();
                        wp.setTakeOffSecurityHeight(Double.parseDouble(takeOffSecurityHeight));
                        wp.setLongitude(parseDoubleSafe(parts[0]));
                        wp.setLatitude(parseDoubleSafe(parts[1]));
                        // altitude from <wpml:height>
                        if (i < heightNodes.size()) {
                            String h = heightNodes.get(i).getText();
                            wp.setAltitude(parseDoubleSafe(h));
                        }
                        // index
                        if (i < indexNodes.size()) {
                            String idx = indexNodes.get(i).getText();
                            wp.setIndex(parseIntSafe(idx));
                        } else {
                            wp.setIndex(i);
                        }
                        // useGlobalSpeed
                        if (i < speedNodes.size()) {
                            wp.setUseGlobalSpeed(parseBoolFlag(speedNodes.get(i).getText()));
                        }
                        // useGlobalHeadingParam -> useGlobalHeading
                        if (i < headingNodes.size()) {
                            wp.setUseGlobalHeading(parseBoolFlag(headingNodes.get(i).getText()));
                        }
                        // gimbalPitchAngle
                        if (i < gimbalNodes.size()) {
                            wp.setGimbalPitchAngle(parseDoubleSafe(gimbalNodes.get(i).getText()));
                        }
                        waypoints.add(wp);
                    }
                }
                return waypoints;
            }

        } catch (IOException | DocumentException e) {
            e.printStackTrace();
            throw new RuntimeException("Failed to parse KMZ waypoint file.", e);
        }
        throw new RuntimeException("The file format is incorrect.");
    }

    private static boolean parseBoolFlag(String text) {
        String t = text == null ? "" : text.trim().toLowerCase();
        return "1".equals(t) || "true".equals(t);
    }

    private static double parseDoubleSafe(String text) {
        try {
            return Double.parseDouble(text.trim());
        } catch (Exception e) {
            return 0.0;
        }
    }

    private static int parseIntSafe(String text) {
        try {
            return Integer.parseInt(text.trim());
        } catch (Exception e) {
            return 0;
        }
    }
}